'''

    typism - http://code.google.com/p/typism/

    Copyright (c) 2008, 2009 Juraj Sukop

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

'''


from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from copy import deepcopy
from os.path import dirname, join
from re import sub
from unicodedata import normalize

template.register_template_library('filters')


import logging
logging.getLogger().setLevel(logging.DEBUG)
##logging.info(...)


class Model(db.Model):
    name = db.StringProperty()
    revision = db.IntegerProperty()
    summary = db.TextProperty()
    author = db.UserProperty()
    datetime = db.DateTimeProperty(auto_now_add=True)
    text = db.TextProperty()
    font = db.TextProperty()


class Handler(webapp.RequestHandler):
    def render(self, filename, name, values):
        path = join(dirname(__file__), 'templates', filename)
        values.update({
            'name': name,
            'user': users.get_current_user(),
            'login': users.create_login_url(self.request.uri),
            'logout': users.create_logout_url(self.request.uri)
        })
        self.response.headers['Content-Type'] = 'application/xhtml+xml'
        self.response.out.write(template.render(path, values))
    def render_entity(self, filename, name, entity):
        self.render(filename, name, {
            'revision': entity.revision,
            'author': entity.author,
            'datetime': entity.datetime,
            'text': entity.text,
            'font': entity.font
        } if entity else {
            'revision': '0',
            'author': None,
            'datetime': None,
            'text': 'This page is empty.',
            'font': None
        })
    def render_paginated(self, filename, name, values, limit, offset, filter, argument, order, ascending):
        if ascending:
            less = ' <'
            notless = ' >='
            sign = ''
            notsign = '-'
        else:
            less = ' >'
            notless = ' <='
            sign = '-'
            notsign = ''
        query = Model.all().filter(filter + ' =', argument)
        if offset:
            prev = deepcopy(query).filter(order + less, offset).order(notsign + order).fetch(limit)
            prev = getattr(prev[-1], order) if len(prev) > 0 else None
            query = query.filter(order + notless, offset)
        else:
            prev = None
        entities = query.order(sign + order).fetch(limit + 1)
        if len(entities) > limit:
            offset = getattr(entities[-1], order)
            entities = entities[:limit]
        else:
            offset = None
        values.update({
            'offset': offset,
            'prev': prev,
            'entities': entities
        })
        self.render(filename, name, values)
    def error(self, code):
        webapp.RequestHandler.error(self, code)
        if code == 404:
            self.response.out.write('404: not found')


class Index(webapp.RequestHandler):
    def get(self):
        self.redirect('/fonts/index')

class Entry(Handler):
    def get(self, name, revision):
        if revision:
            entity = Model.all().filter('name =', name).filter('revision =', int(revision)).get()
            if not entity:
                self.error(404)
                return
        else:
            entity = Model.all().filter('name =', name).order('-revision').get()
        self.render_entity('entry.xhtml', name, entity)

class History(Handler):
    def get(self, name):
        revision = getattr(Model.all().filter('name =', name).order('-revision').get(), 'revision', '0')
        try:
            offset = int(self.request.get('offset'))
        except ValueError:
            offset = None
        self.render_paginated('history.xhtml', name, { 'revision': revision }, 20, offset, 'name', name, 'revision', False)

class Editor(Handler):
    def get(self, name, revision):
        if revision == '0':
            self.render_entity('editor.xhtml', name, None)
        else:
            entity = Model.all().filter('name =', name).filter('revision =', int(revision)).get()
            if entity:
                self.render_entity('editor.xhtml', name, entity)
            else:
                self.error(404)
    def post(self, name, revision):
        user = users.get_current_user()
        if user:
            text = self.request.get('text')
            font = self.request.get('font')
            if text and font:
                latest = Model.all().filter('name =', name).order('-revision').get()
            else:
                query = Model.all().filter('name =', name)
                entity = deepcopy(query).filter('revision =', int(revision)).get()
                if entity:
                    if not text:
                        text = entity.text
                    if not font:
                        font = entity.font
                else:
                    self.error(404)
                    return
                latest = query.order('-revision').get()
            Model(
                name = name,
                revision = latest.revision + 1 if latest else 1,
                summary = self.request.get('summary'),
                author = user,
                text = text,
                font = font
            ).put()
            self.redirect('/fonts/' + name)
        else:
            self.redirect(users.create_login_url(self.request.uri))

class Font(webapp.RequestHandler):
    def get(self, name, revision):
        entity = Model.all().filter('name =', name).filter('revision =', int(revision)).get()
        if entity:
            self.response.headers['Content-Type'] = 'image/svg+xml'
            self.response.headers['Content-Disposition'] = 'attachment; filename=%s.%s.svg' % (name, revision)
            self.response.out.write('<?xml version="1.0" standalone="yes"?>')
            self.response.out.write(entity.font)
        else:
            self.error(404)

class Atom(webapp.RequestHandler):
    def get(self, name):
        limit = 10
        entities = Model.all().filter('name =', name).order('-revision').fetch(limit)
        if len(entities) > 0:
            path = join(dirname(__file__), 'templates', 'atom.xml')
            self.response.headers['Content-Type'] = 'application/atom+xml'
            self.response.out.write(template.render(path, {
                'name': name,
                'host': self.request.host_url,
                'entities': entities
            }))
        else:
            self.error(404)

class Auto(Handler):
    def get(self, action):
        if action == 'new':
            self.render('auto-new.xhtml', 'new', {})
        else:
            self.render_paginated('auto-list.xhtml', 'list', {}, 50, self.request.get('offset'), 'revision', 1, 'name', True)
    def post(self, action):
        if action == 'new':
            name = self.request.get('name')
            name = normalize('NFKD', name).encode('ascii', 'ignore').strip().lower()
            name = sub('[^a-z0-9-\s]', '', name)
            name = sub('[-\s]+', '-', name)
            self.redirect('/fonts/' + name if name else '/auto/new')


application = webapp.WSGIApplication([
    ('/', Index),
    ('/fonts/([a-z0-9-]+)(?:/history/([0-9]+))?', Entry),
    ('/fonts/([a-z0-9-]+)/history', History),
    ('/fonts/([a-z0-9-]+)/history/([0-9]+)/editor', Editor),
    ('/fonts/([a-z0-9-]+)\.([0-9]+)\.svg', Font),
    ('/fonts/([a-z0-9-]+)\.atom', Atom),
    ('/auto/(new|list)', Auto)
], debug=True)

if __name__ == '__main__':
    run_wsgi_app(application)

